package view_server

import (
	"bigdevops/src/cache"
	"bigdevops/src/common"
	"bigdevops/src/config"
	"bigdevops/src/models"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/util/duration"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/client-go/kubernetes"
	resourcehelper "k8s.io/kubectl/pkg/util/resource"
	metricsclientset "k8s.io/metrics/pkg/client/clientset/versioned"
	"log"
	"sigs.k8s.io/yaml"
	"sort"
	"strconv"
	"strings"
	"time"
)

type K8sClusterNodesRequest struct {
	ClusterName string   `json:"clusterName"  validate:"required"`
	NodeNames   []string `json:"nodeNames"   validate:"required"`
}

type LabelK8sNodesRequest struct {
	*K8sClusterNodesRequest
	// k=v
	Labels []string `json:"labels"`
}

type TaintK8sNodesRequest struct {
	*K8sClusterNodesRequest
	// k=v
	TaintYamlString string `json:"taintYamlString"`
	// modType=add del 代表删除
	ModType string `json:"modType"`
}

type K8sOneNode struct {
	// 分类：list 列表要的数据
	Name           string   `json:"name"`
	Status         string   `json:"status"`
	ScheduleEnable bool     `json:"scheduleEnable"`
	Roles          []string `json:"roles"`
	Age            string   `json:"age"`
	Ip             string   `json:"ip"`

	// 关于资源的
	PodNum int `json:"podNum"`

	// requestSum/total (requestRate%)
	// 10/50(20%)
	CpuRequestInfo string `json:"cpuRequestInfo"`
	CpuLimitInfo   string `json:"cpuLimitInfo"`
	CpuUsageInfo   string `json:"cpuUsageInfo"`

	MemoryRequestInfo string `json:"memoryRequestInfo"`
	MemoryLimitInfo   string `json:"memoryLimitInfo"`
	MemoryUsageInfo   string `json:"memoryUsageInfo"`

	// podNum/total (rate%)
	// 10/50(20%)
	PodNumInfo string `json:"podNumInfo"`

	// allocatable/capacity
	CpuCores         string `json:"cpuCores"`
	MemGibs          string `json:"memGibs"`
	EphemeralStorage string `json:"ephemeralStorage"`

	// 几个版本
	KubeletVersion string `json:"kubeletVersion"`
	CriVersion     string `json:"criVersion"`
	OsVersion      string `json:"osVersion"`
	KernelVersion  string `json:"kernelVersion"`

	Labels      []string `json:"labels"`
	LabelsFront string   `json:"labelsFront"`
	TaintsFront string   `json:"taintsFront"`

	//
	LabelPairs map[string]string      `json:"labelPairs"`
	Annotation map[string]string      `json:"annotation"`
	Conditions []corev1.NodeCondition `json:"conditions"`
	Taints     []corev1.Taint         `json:"taints"`

	//Events []corev1.Event `json:"events"`
	Events []OneEvent `json:"events"`
}

type OneEvent struct {
	Type      string `json:"type"`
	Component string `json:"component"`
	Reason    string `json:"reason"`
	Message   string `json:"message"`
	FirstTime string `json:"firstTime"`
	LastTime  string `json:"lastTime"`
	Object    string `json:"object"`
	Count     int    `json:"count"`
}

func scheduleEnableSwitchK8sNodes(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	var reqObj K8sClusterNodesRequest
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析k8s集群节点调度状态变化配置请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	dbObj, err := models.GetK8sClusterByName(reqObj.ClusterName)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", reqObj.ClusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", reqObj.ClusterName), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	for _, nodeName := range reqObj.NodeNames {
		ctx1, cancel1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
		defer cancel1()
		kNode, err := kSet.CoreV1().Nodes().Get(ctx1, nodeName, metav1.GetOptions{})
		if err != nil {
			msg := "k8s节点调度开关获取节点错误"
			sc.Logger.Error(msg,
				zap.Any("k8s集群", reqObj.ClusterName),
				zap.Any("k8s节点", nodeName),
				zap.Error(err),
			)
			err = fmt.Errorf("%s %w", msg, err)
			common.FailWithMessage(err.Error(), c)
			return
		}
		// 取反
		kNode.Spec.Unschedulable = !kNode.Spec.Unschedulable

		kNode, err = kSet.CoreV1().Nodes().Update(ctx1, kNode, metav1.UpdateOptions{})
		if err != nil {
			msg := "k8s节点调度开关调度节点错误"
			sc.Logger.Error(msg,
				zap.Any("k8s集群", reqObj.ClusterName),
				zap.Any("k8s节点", nodeName),
				zap.Error(err),
			)
			err = fmt.Errorf("%s %w", msg, err)
			common.FailWithMessage(err.Error(), c)
			return
		}
	}

	common.OkWithMessage("更新成功", c)

}

func labelK8sNodes(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	var reqObj LabelK8sNodesRequest
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析k8s集群节点标签配置请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	dbObj, err := models.GetK8sClusterByName(reqObj.ClusterName)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", reqObj.ClusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", reqObj.ClusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 先把标签解析成map
	lm := common.GenMapByKvStrings(reqObj.Labels)

	for _, nodeName := range reqObj.NodeNames {
		ctx1, cancel1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
		defer cancel1()
		kNode, err := kSet.CoreV1().Nodes().Get(ctx1, nodeName, metav1.GetOptions{})
		if err != nil {
			msg := "k8s节点标签获取节点错误"
			sc.Logger.Error(msg,
				zap.Any("k8s集群", reqObj.ClusterName),
				zap.Any("k8s节点", nodeName),
				zap.Error(err),
			)
			err = fmt.Errorf("%s %w", msg, err)
			common.FailWithMessage(err.Error(), c)
			return
		}

		// 标签操作
		kNodeLabels := kNode.Labels
		if kNodeLabels == nil {
			kNodeLabels = map[string]string{}
		}
		for k, v := range lm {
			kNodeLabels[k] = v
		}
		kNode.SetLabels(kNodeLabels)

		kNode, err = kSet.CoreV1().Nodes().Update(ctx1, kNode, metav1.UpdateOptions{})
		if err != nil {
			msg := "k8s节点更新节点标签错误"
			sc.Logger.Error(msg,
				zap.Any("k8s集群", reqObj.ClusterName),
				zap.Any("k8s节点", nodeName),
				zap.Error(err),
			)
			err = fmt.Errorf("%s %w", msg, err)
			common.FailWithMessage(err.Error(), c)
			return
		}
	}

	common.OkWithMessage("更新成功", c)

}

func taintYamlCheck(c *gin.Context) {
	//sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	taintYaml := c.DefaultQuery("taintYaml", "")
	_, err := yamlParseTaintString(taintYaml)
	cr := common.CommonCheckResult{
		Success: true,
		Err:     "",
	}
	if err != nil {
		cr.Err = err.Error()
		cr.Success = false
	}
	common.OkWithDetailed(cr, "ok", c)
}

func yamlParseTaintString(ts string) ([]corev1.Taint, error) {
	var t []corev1.Taint
	err := yaml.UnmarshalStrict([]byte(ts), &t)
	return t, err
}

func taintsK8sNodes(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	var reqObj TaintK8sNodesRequest
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析k8s集群节点污点配置请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	dbObj, err := models.GetK8sClusterByName(reqObj.ClusterName)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", reqObj.ClusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", reqObj.ClusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 解析一下污点配置
	taints, err := yamlParseTaintString(reqObj.TaintYamlString)
	if err != nil {
		sc.Logger.Error("解析一下污点配置错误", zap.Any("k8s集群配置", reqObj.ClusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	for _, nodeName := range reqObj.NodeNames {
		ctx1, cancel1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
		defer cancel1()
		kNode, err := kSet.CoreV1().Nodes().Get(ctx1, nodeName, metav1.GetOptions{})
		if err != nil {
			msg := "k8s节点标签获取节点错误"
			sc.Logger.Error(msg,
				zap.Any("k8s集群", reqObj.ClusterName),
				zap.Any("k8s节点", nodeName),
				zap.Error(err),
			)
			err = fmt.Errorf("%s %w", msg, err)
			common.FailWithMessage(err.Error(), c)
			return
		}

		// 追加写入，不要覆盖
		switch reqObj.ModType {
		case "add":

			kNode.Spec.Taints = append(kNode.Spec.Taints, taints...)
			taintsM := map[string]corev1.Taint{}
			for _, t := range kNode.Spec.Taints {
				key := fmt.Sprintf("%s_%s_%s",
					t.Key,
					t.Value,
					t.Effect,
				)
				taintsM[key] = t
			}
			ts := []corev1.Taint{}
			for _, v := range taintsM {
				ts = append(ts, v)
			}
			kNode.Spec.Taints = ts
		case "del":
			taintsM := map[string]corev1.Taint{}
			for _, t := range kNode.Spec.Taints {

				key := fmt.Sprintf("%s_%s_%s",
					t.Key,
					t.Value,
					t.Effect,
				)
				taintsM[key] = t
			}
			for _, t := range taints {
				key := fmt.Sprintf("%s_%s_%s",
					t.Key,
					t.Value,
					t.Effect,
				)
				delete(taintsM, key)

			}
			ts := []corev1.Taint{}
			for _, v := range taintsM {
				ts = append(ts, v)
			}
			kNode.Spec.Taints = ts
		}

		kNode, err = kSet.CoreV1().Nodes().Update(ctx1, kNode, metav1.UpdateOptions{})
		if err != nil {
			msg := "k8s节点更新节点标签错误"
			sc.Logger.Error(msg,
				zap.Any("k8s集群", reqObj.ClusterName),
				zap.Any("k8s节点", nodeName),
				zap.Error(err),
			)
			err = fmt.Errorf("%s %w", msg, err)
			common.FailWithMessage(err.Error(), c)
			return
		}
	}

	common.OkWithMessage("更新成功", c)

}

func drainK8sNodes(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	var reqObj K8sClusterNodesRequest
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析k8s集群 驱逐节点请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	dbObj, err := models.GetK8sClusterByName(reqObj.ClusterName)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", reqObj.ClusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", reqObj.ClusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	drainPodsResults := ""
	for _, nodeName := range reqObj.NodeNames {
		ctx1, cancel1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
		defer cancel1()
		kNode, err := kSet.CoreV1().Nodes().Get(ctx1, nodeName, metav1.GetOptions{})
		if err != nil {
			msg := "k8s节点标签获取节点错误"
			sc.Logger.Error(msg,
				zap.Any("k8s集群", reqObj.ClusterName),
				zap.Any("k8s节点", nodeName),
				zap.Error(err),
			)
			err = fmt.Errorf("%s %w", msg, err)
			common.FailWithMessage(err.Error(), c)
			return
		}

		// 设置不可调度
		kNode.Spec.Unschedulable = true

		kNode, err = kSet.CoreV1().Nodes().Update(ctx1, kNode, metav1.UpdateOptions{})
		if err != nil {
			msg := "驱逐k8s节点更新调度状态错误"
			sc.Logger.Error(msg,
				zap.Any("k8s集群", reqObj.ClusterName),
				zap.Any("k8s节点", nodeName),
				zap.Error(err),
			)
			err = fmt.Errorf("%s %w", msg, err)
			common.FailWithMessage(err.Error(), c)
			return
		}

		// 再根据 获取这个节点上的pod
		pods, err := getPodsOnNode(kNode.Name, dbObj.ActionTimeoutSeconds, kSet)
		if err != nil {

			msg := "驱逐节点 k8s节点找pod错误"
			sc.Logger.Error(msg,
				zap.Any("k8s集群", reqObj.ClusterName),
				zap.Any("k8s节点", nodeName),
				zap.Error(err),
			)
			err = fmt.Errorf("%s %w", msg, err)
			common.FailWithMessage(err.Error(), c)
			return
		}
		// 遍历pods 删除即可

		for _, pod := range pods {
			dsManaged := false

			for _, owner := range pod.OwnerReferences {
				if owner.Kind == "DaemonSet" {
					dsManaged = true
					break
				}
			}
			// 如果是被ds管理的忽略它
			if dsManaged {
				continue
			}
			ctx2, cancel2 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
			defer cancel2()
			var gracePeriodSeconds int64 = -1
			var value *int64
			value = &gracePeriodSeconds
			err = kSet.CoreV1().Pods(pod.Namespace).Delete(ctx2, pod.Name, metav1.DeleteOptions{
				GracePeriodSeconds: value,
			})

			msg := fmt.Sprintf("成功驱逐：node:%v ns:%v pod:%v\n", kNode.Name, pod.Namespace, pod.Name)
			if err != nil {
				msg = fmt.Sprintf("驱逐节点上的pod错误：node:%v ns:%v pod:%v\n", kNode.Name, pod.Namespace, pod.Name)
				sc.Logger.Error(msg,
					zap.Any("k8s集群", reqObj.ClusterName),
					zap.Any("k8s节点", nodeName),
					zap.Any("pod", pod.Name),
					zap.Error(err),
				)
			}
			drainPodsResults += msg

		}

	}

	common.OkWithData(drainPodsResults, c)

}

func getK8sNodeList(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	currentPage, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
	sortFiled := c.DefaultQuery("field", "")
	orderType := c.DefaultQuery("order", "")
	//offset := 0
	//limit := 0
	//limit = pageSize
	//if currentPage > 1 {
	//	offset = (currentPage - 1) * limit
	//}
	//
	//fmt.Println(offset)
	// 这里只能新增 不能去掉，因为是 多种资源共用的query
	//searchUserID := c.DefaultQuery("UserID", "")
	//searchUserIDInt, _ := strconv.Atoi(searchUserID)
	//searchName := c.DefaultQuery("name", "")
	// 集群的名称
	searchCluster := c.DefaultQuery("cluster", "bigdevops")
	searchName := c.DefaultQuery("name", "")

	if searchCluster == "" {
		msg := "集群名称为传入"
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	mSet := kc.GetClusterMetricsClientSetById(dbObj.ID)
	if mSet == nil {
		sc.Logger.Error("根据id找k8s集群metricsSet缓存错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	ctx1, cancel1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
	defer cancel1()
	nodes, err := kSet.CoreV1().Nodes().List(ctx1, metav1.ListOptions{})
	if err != nil {
		sc.Logger.Error("根据ik8s集群kset获取节点错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 进行分页
	filterNodes := []corev1.Node{}
	for _, node := range nodes.Items {
		if searchName != "" && !strings.Contains(node.Name, searchName) {
			continue
		}
		filterNodes = append(filterNodes, node)

	}

	//startIndex, endIndex
	startIndex := (pageSize) * (currentPage - 1)
	endIndex := startIndex + pageSize
	if endIndex > len(filterNodes) {
		endIndex = len(filterNodes)
	}
	log.Printf("startIndex :%v endIndex:%v len(nodes.Items):%v",
		startIndex,
		endIndex,
		len(filterNodes),
	)
	// 转化一下
	mNodes := []*K8sOneNode{}
	pageResult := filterNodes[startIndex:endIndex]

	for _, node := range pageResult {
		mNode := nodeConvert(node, dbObj, kSet, mSet, false)
		mNodes = append(mNodes, mNode)
	}
	// 排序
	if sortFiled == "podNum" {

		sort.SliceStable(mNodes, func(i, j int) bool {
			if orderType == "ascend" {
				return mNodes[i].PodNum < mNodes[j].PodNum
			} else {
				return mNodes[i].PodNum > mNodes[j].PodNum
			}

		})
	}

	resp := &ResponseResourceCommon{
		//Total: models.GetK8sClusterCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

		Total: len(filterNodes),
		Items: mNodes,
	}

	common.OkWithDetailed(resp, "ok", c)
}

func getK8sNodeDetail(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	clusterName := c.DefaultQuery("clusterName", "")
	nodeName := c.DefaultQuery("nodeName", "")

	dbObj, err := models.GetK8sClusterByName(clusterName)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", clusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		sc.Logger.Error("根据id找k8s集群kset缓存错误", zap.Any("k8s集群配置", clusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	mSet := kc.GetClusterMetricsClientSetById(dbObj.ID)
	if mSet == nil {
		sc.Logger.Error("根据id找k8s集群metricsSet缓存错误", zap.Any("k8s集群配置", clusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	ctx1, cancel1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
	defer cancel1()
	kNode, err := kSet.CoreV1().Nodes().Get(ctx1, nodeName, metav1.GetOptions{})
	if err != nil {
		msg := "获取k8s节点详情获取节点错误"
		sc.Logger.Error(msg,
			zap.Any("k8s集群", clusterName),
			zap.Any("k8s节点", nodeName),
			zap.Error(err),
		)
		err = fmt.Errorf("%s %w", msg, err)
		common.FailWithMessage(err.Error(), c)
		return
	}

	mNode := nodeConvert(*kNode, dbObj, kSet, mSet, true)

	common.OkWithData(mNode, c)

}

// 计算节点上的pod数量

func getPodsListByNodeName(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	clusterName := c.DefaultQuery("clusterName", "")
	nodeName := c.DefaultQuery("nodeName", "")

	currentPage, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
	dbObj, err := models.GetK8sClusterByName(clusterName)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", clusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", clusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	mSet := kc.GetClusterMetricsClientSetById(dbObj.ID)
	if mSet == nil {
		sc.Logger.Error("根据id找k8s集群metricsSet缓存错误", zap.Any("k8s集群配置", clusterName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	ctx1, cancel1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
	defer cancel1()
	kNode, err := kSet.CoreV1().Nodes().Get(ctx1, nodeName, metav1.GetOptions{})
	if err != nil {
		msg := "获取k8s节点详情获取节点错误"
		sc.Logger.Error(msg,
			zap.Any("k8s集群", clusterName),
			zap.Any("k8s节点", nodeName),
			zap.Error(err),
		)
		err = fmt.Errorf("%s %w", msg, err)
		common.FailWithMessage(err.Error(), c)
		return
	}
	pods, err := getPodsOnNode(kNode.Name, dbObj.ActionTimeoutSeconds, kSet)
	if err != nil {

		msg := "k8s节点找pod错误"
		sc.Logger.Error(msg,
			zap.Any("k8s集群", clusterName),
			zap.Any("k8s节点", nodeName),
			zap.Error(err),
		)
		err = fmt.Errorf("%s %w", msg, err)
		common.FailWithMessage(err.Error(), c)
		return
	}
	// 进行分页

	//startIndex, endIndex
	startIndex := (pageSize) * (currentPage - 1)
	endIndex := startIndex + pageSize
	if endIndex > len(pods) {
		endIndex = len(pods)
	}
	target := pods[startIndex:endIndex]
	myPods := []*OnePod{}
	for _, pod := range target {
		myPod := podConvert(pod)
		myPods = append(myPods, myPod)
	}

	resp := &ResponseResourceCommon{
		//Total: models.GetK8sClusterCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

		Total: len(pods),
		Items: myPods,
	}
	common.OkWithDetailed(resp, "ok", c)

}

func getPodsOnNode(nodeName string, tw int, kSet *kubernetes.Clientset) ([]corev1.Pod, error) {

	ctx1, cancel1 := common.GenTimeoutContext(tw)
	defer cancel1()

	fieldSelector, _ := fields.ParseSelector(fmt.Sprintf("spec.nodeName=%v", nodeName))
	res, err := kSet.CoreV1().Pods("").List(ctx1, metav1.ListOptions{
		FieldSelector: fieldSelector.String(),
	})
	return res.Items, err

}

func getEventsOnNode(nodeName string, tw int, kSet *kubernetes.Clientset) ([]corev1.Event, error) {

	ctx1, cancel1 := common.GenTimeoutContext(tw)
	defer cancel1()

	// fieldSelector: involvedObject.namespace=,involvedObject.kind=Node,involvedObject.uid=k8s-master01,involvedObject.name=k8s-master01
	fieldSelector, _ := fields.ParseSelector(fmt.Sprintf("involvedObject.name=%v", nodeName))
	res, err := kSet.CoreV1().Events("default").List(ctx1, metav1.ListOptions{
		FieldSelector: fieldSelector.String(),
	})
	return res.Items, err

}

// 封装转换方法： k8s node --> myNode

func nodeConvert(kNode corev1.Node, kCluster *models.K8sCluster, kSet *kubernetes.Clientset, mSet *metricsclientset.Clientset, fetchEvent bool) *K8sOneNode {

	res := &K8sOneNode{}
	res.Name = kNode.Name

	// 获取节点状态
	conditionMap := make(map[corev1.NodeConditionType]*corev1.NodeCondition)
	NodeAllConditions := []corev1.NodeConditionType{corev1.NodeReady}
	for i := range kNode.Status.Conditions {
		cond := kNode.Status.Conditions[i]
		conditionMap[cond.Type] = &cond
	}
	var status []string
	for _, validCondition := range NodeAllConditions {
		if condition, ok := conditionMap[validCondition]; ok {
			if condition.Status == corev1.ConditionTrue {
				status = append(status, string(condition.Type))
			} else {
				status = append(status, "Not"+string(condition.Type))
			}
		}
	}
	if len(status) == 0 {
		status = append(status, "Unknown")
	}
	if kNode.Spec.Unschedulable {
		status = append(status, "SchedulingDisabled")
	}
	res.Status = strings.Join(status, ",")

	res.Roles = findNodeRoles(kNode)

	res.ScheduleEnable = !kNode.Spec.Unschedulable

	res.Age = translateTimestampSince(kNode.CreationTimestamp)

	res.Ip = getNodeInternalIP(kNode)

	osImage, kernelVersion, crVersion := kNode.Status.NodeInfo.OSImage, kNode.Status.NodeInfo.KernelVersion, kNode.Status.NodeInfo.ContainerRuntimeVersion
	kubeletVersion := kNode.Status.NodeInfo.KubeletVersion
	if osImage == "" {
		osImage = "<unknown>"
	}
	if kernelVersion == "" {
		kernelVersion = "<unknown>"
	}
	if crVersion == "" {
		crVersion = "<unknown>"
	}
	if kubeletVersion == "" {
		kubeletVersion = "<unknown>"
	}

	res.OsVersion = osImage
	res.KernelVersion = kernelVersion
	res.CriVersion = crVersion
	res.KubeletVersion = kubeletVersion

	// 标签
	res.LabelPairs = kNode.Labels
	res.Labels = common.GenStringArrayByMap(kNode.Labels)
	res.LabelsFront = strings.Join(res.Labels, "\n")
	// 污点
	res.Taints = kNode.Spec.Taints
	ts := ""
	for _, t := range kNode.Spec.Taints {
		oneT := fmt.Sprintf("key:%s value:%s effect:%s", t.Key, t.Value, t.Effect)
		//oneT := t.String()
		ts = fmt.Sprintf("%s\n%s", ts, oneT)
	}
	res.TaintsFront = ts

	res.Conditions = kNode.Status.Conditions

	// event 处理
	if fetchEvent {
		events, _ := getEventsOnNode(kNode.Name, kCluster.ActionTimeoutSeconds, kSet)
		myEvents := []OneEvent{}

		for _, event := range events {
			onEvent := OneEvent{
				Type:      event.Type,
				Component: event.Source.Component,
				Reason:    event.Reason,
				Message:   event.Message,
				FirstTime: event.FirstTimestamp.String(),
				LastTime:  event.LastTimestamp.String(),
				Object:    fmt.Sprintf("kind:%v name:%v", event.InvolvedObject.Kind, event.InvolvedObject.Name),
				Count:     int(event.Count),
			}
			myEvents = append(myEvents, onEvent)

		}

		res.Events = myEvents
	}

	// 计算pod数量
	pods, _ := getPodsOnNode(kNode.Name, kCluster.ActionTimeoutSeconds, kSet)
	res.PodNum = len(pods)

	//var cpuRequestTotal, cpuLimitTotal, memRequestTotal, memLimitTotal int64
	// 计算资源

	reqs, limits := map[corev1.ResourceName]resource.Quantity{}, map[corev1.ResourceName]resource.Quantity{}

	for _, pod := range pods {

		podReqs, podLimits := resourcehelper.PodRequestsAndLimits(&pod)

		for podReqName, podReqValue := range podReqs {
			if value, ok := reqs[podReqName]; !ok {
				reqs[podReqName] = podReqValue.DeepCopy()
			} else {
				value.Add(podReqValue)
				reqs[podReqName] = value
			}
		}
		for podLimitName, podLimitValue := range podLimits {
			if value, ok := limits[podLimitName]; !ok {
				limits[podLimitName] = podLimitValue.DeepCopy()
			} else {
				value.Add(podLimitValue)
				limits[podLimitName] = value
			}
		}
		//log.Printf("获取节点上的pod信息 %v:%v %v creq:%v", index, kNode.Name, pod.Name, c.Resources.Requests.Cpu().MilliValue())

	}

	cpuReqs, cpuLimits, memoryReqs, memoryLimits := reqs[corev1.ResourceCPU], limits[corev1.ResourceCPU], reqs[corev1.ResourceMemory], limits[corev1.ResourceMemory]
	fractionCpuReqs := float64(0)
	fractionCpuLimits := float64(0)

	allocatable := kNode.Status.Allocatable

	if allocatable.Cpu().MilliValue() != 0 {
		fractionCpuReqs = float64(cpuReqs.MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
		fractionCpuLimits = float64(cpuLimits.MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
	}

	fractionMemoryReqs := float64(0)
	fractionMemoryLimits := float64(0)
	if allocatable.Memory().Value() != 0 {
		fractionMemoryReqs = float64(memoryReqs.Value()) / float64(allocatable.Memory().Value()) * 100
		fractionMemoryLimits = float64(memoryLimits.Value()) / float64(allocatable.Memory().Value()) * 100
	}

	//allocatableMemory := kNode.Status.Capacity.Memory().Value()
	// 基础资源
	res.CpuCores = fmt.Sprintf("%v/%v", allocatable.Cpu().String(), kNode.Status.Capacity.Cpu().String())
	res.MemGibs = fmt.Sprintf("%v/%v", kNode.Status.Capacity.Memory().Value()/1024/1024, kNode.Status.Capacity.Memory().Value()/1024/1024)
	res.EphemeralStorage = fmt.Sprintf("%v/%v", kNode.Status.Allocatable.StorageEphemeral().String(), kNode.Status.Capacity.StorageEphemeral().String())

	// 申请和limit
	res.CpuRequestInfo = fmt.Sprintf("%v/%v(%v%%)", cpuReqs.String(), allocatable.Cpu().String(), int64(fractionCpuReqs))
	res.CpuLimitInfo = fmt.Sprintf("%v/%v(%v%%)", cpuLimits.String(), allocatable.Cpu().String(), int64(fractionCpuLimits))
	res.MemoryRequestInfo = fmt.Sprintf("%v/%v(%v%%)", memoryReqs.String(), allocatable.Memory().Value()/1024/1024/1024, int64(fractionMemoryReqs))
	res.MemoryLimitInfo = fmt.Sprintf("%v/%v(%v%%)", memoryLimits.String(), allocatable.Memory().Value()/1024/1024/1024, int64(fractionMemoryLimits))

	// 节点的使用率
	//cpuUsages:=

	fractionCpuUsages := float64(0)
	fractionMemoryUsages := float64(0)
	var cpuUsages int64 = 0
	var memoryUsages int64 = 0
	nodeUsage, _ := getNodeUsageByMetricsServer(kNode.Name, kCluster.ActionTimeoutSeconds, mSet)
	if nodeUsage != nil {
		cpuUsages = nodeUsage.Cpu().MilliValue()
		memoryUsages = nodeUsage.Memory().Value() / 1024 / 1024
		fractionCpuUsages = float64(nodeUsage.Cpu().MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
		fractionMemoryUsages = float64(nodeUsage.Memory().Value()) / float64(allocatable.Memory().Value()) * 100
	}

	res.CpuUsageInfo = fmt.Sprintf("%vm/%v(%v%%)", cpuUsages, allocatable.Cpu().String(), int64(fractionCpuUsages))
	res.MemoryUsageInfo = fmt.Sprintf("%vMi/%v(%v%%)", memoryUsages, allocatable.Memory().Value()/1024/1024/1024, int64(fractionMemoryUsages))
	return res

}

func div(a, b int64) string {
	return fmt.Sprintf("%.1f", 100*float64(a)/float64(b))
}

// Returns the internal IP of the node or "<none>" if none is found.
func getNodeInternalIP(node corev1.Node) string {
	for _, address := range node.Status.Addresses {
		if address.Type == corev1.NodeInternalIP {
			return address.Address
		}
	}

	return "<none>"
}

func getNodeUsageByMetricsServer(nodeName string, tw int, mSet *metricsclientset.Clientset) (corev1.ResourceList, error) {
	ctx1, cancel1 := common.GenTimeoutContext(tw)
	defer cancel1()
	nodeMetrics, err := mSet.MetricsV1beta1().NodeMetricses().Get(ctx1, nodeName, metav1.GetOptions{})
	return nodeMetrics.Usage, err
}

func translateTimestampSince(timestamp metav1.Time) string {
	if timestamp.IsZero() {
		return "<unknown>"
	}

	return duration.HumanDuration(time.Since(timestamp.Time))
}

// findNodeRoles returns the roles of a given node.
// The roles are determined by looking for:
// * a node-role.kubernetes.io/<role>="" label
// * a kubernetes.io/role="<role>" label
func findNodeRoles(node corev1.Node) []string {
	roles := sets.NewString()
	for k, v := range node.Labels {
		switch {
		case strings.HasPrefix(k, common.LabelNodeRolePrefix):
			if role := strings.TrimPrefix(k, common.LabelNodeRolePrefix); len(role) > 0 {
				roles.Insert(role)
			}

		case k == common.NodeLabelRole && v != "":
			roles.Insert(v)
		}
	}
	return roles.List()
}
